﻿using System;
using System.Globalization;
using System.Linq;
using System.Web.Mvc;
using System.Xml.Linq;
using TeamsPortal.ServiceClient;
using TeamsPortal.Validation;
using TeamsPortal.Web.Controllers.Filters;
using TeamsPortal.Web.Controllers.Helpers;
using TeamsPortal.Web.Extensions;
using TeamsPortal.DataAccess;

namespace TeamsPortal.Web.Controllers
{
	public class TopicsController : BaseController
	{
        private readonly ITopicsServiceClient _topicServiceClient;
        private readonly IMessagesServiceClient _messageServiceClient;
        private readonly IForumsServiceClient _forumsServiceClient;
        private readonly ITopicsSubscriptionsServiceClient _subscriptionServiceClient;

        public TopicsController(ITopicsServiceClient topicServiceClient, 
            IMessagesServiceClient messageServiceClient
            , IForumsServiceClient forumsServiceClient
            , ITopicsSubscriptionsServiceClient subscriptionServiceClient
            )
        {
            _topicServiceClient = topicServiceClient;
            _messageServiceClient = messageServiceClient;
            _forumsServiceClient = forumsServiceClient;
            _subscriptionServiceClient = subscriptionServiceClient;
        }

		#region Detail
        [AddVisit]
		[ValidateReadAccess]
		public ActionResult Detail(int id, string name, string forum, int page)
		{
            var topic = _topicServiceClient.Get(id, name);

			if (topic == null)
			{
				return ResultHelper.NotFoundResult(this);
			}
			if (topic.Forum.ShortName != forum)
			{
				//The topic could have been moved to another forum
				//Move permanently to the other forum's topic
                return RedirectToActionPermanent("Detail", "Topics", new { id = id, name = name, forum = topic.Forum.ShortName });
			}

            int totalMessages;
            topic.Messages = _messageServiceClient.GetByTopic(id, User != null ? User.Id : new Nullable<int>(), Config.UI.MessagesPerPage, page, out totalMessages);
            ViewBag.TotalMessages = totalMessages;

            _topicServiceClient.LoadRelatedTopics(topic, 5);

			ViewData["Page"] = page;
			//Defines that the message url should be full
			ViewData["FullUrl"] = true;

			return View(topic);
		} 
		#endregion

		#region Latest Messages
		[ValidateReadAccess]
		public ActionResult LatestMessages(int id, string name)
		{
			var topic = _topicServiceClient.Get(id, name);
			if (topic == null)
			{
				return ResultHelper.NotFoundResult(this);
			}

			topic.Messages = _messageServiceClient.GetByTopicLatest(id);

            var list = from m in topic.Messages
                       select new XElement("item", 
                           new XElement("title", string.Format("#{0} {1} {2}", m.Id, S("by"), m.User.UserName)),
                           new XElement("link", string.Format("{0}{1}#msg{2}", this.Domain, Url.Action("ShortUrl", new { id = topic.Id }), m.Id)),
                           new XElement("guid", string.Format("{0}{1}#msg{2}", this.Domain, Url.Action("ShortUrl", new { id = topic.Id }), m.Id)),
                           new XElement("description", Utils.RemoveTags(m.Body)),
                           new XElement("pubDate", m.Date.ToApplicationDateTime().ToString("r", new CultureInfo("en-US")))
                           );
            var doc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
                new XElement("rss", 
                    new XAttribute("version", "2.0"),
                    new XElement("channel",
                        new XElement("title", S("Latest messages on '{0}' thread", topic.Title)),
                        new XElement("link", string.Format("{0}{1}", this.Domain, Url.RouteUrl(new { action = "Detail", 
                                                                                                    controller = "Topics", 
                                                                                                    id = topic.Id, 
                                                                                                    forum = topic.Forum.ShortName, 
                                                                                                    name = topic.ShortName }))),
                        new XElement("description", Utils.RemoveTags(topic.Description)),
                        list.ToList<XElement>()
                        )
                    )
            );

            return Content(doc.Declaration.ToString() + doc.ToString(SaveOptions.DisableFormatting), "text/xml");
		}
		#endregion

		#region Add
		[HttpGet]
		[RequireAuthorization]
		[PreventFlood]
		public ActionResult Add(string forum)
		{
            Forum f = _forumsServiceClient.Get(forum);
			if (!f.HasPostAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}

			var topic = new Topic();
			topic.Forum = f;
			//Default the right access to its parent. If less it will be overriden.
			topic.ReadAccessRole = f.ReadAccessRole;
			//Default, It can be less than its parent
			topic.PostAccessRole = f.PostAccessRole;
            var roles = UsersServiceClient.GetRoles().Where(x => x.Key <= Role);
			ViewBag.UserRoles = new SelectList(roles, "Key", "Value");
			
            return View("Edit", topic);
		}

		[HttpPost]
		[RequireAuthorization]
		[ValidateInput(false)]
		[PreventFlood(typeof(RedirectToRouteResult))]
		[ValidateAntiForgeryToken]
		public ActionResult Add(string forum, [Bind(Prefix = "", Exclude = "Id,Forum")] Topic topic, bool notify, string email)
		{
			try
			{
				SubscriptionHelper.SetNotificationEmail(notify, email, Session, Config, UsersServiceClient);

                topic.Forum = _forumsServiceClient.Get(forum);
				if (topic.Forum == null)
				{
					return ResultHelper.NotFoundResult(this);
				}
				if (!topic.Forum.HasPostAccess(Role))
				{
					return ResultHelper.ForbiddenResult(this);
				}
                
                topic.User = new User(User.Id, User.UserName);
                //http://stackoverflow.com/questions/5682160/a-potentially-dangerous-request-path-value-was-detected-from-the-client
                topic.ShortName = topic.Title.ToUrlSegment(64);
				topic.IsSticky = (topic.IsSticky && this.User.Role >= UserRole.Moderator);
				if (topic.Description != null)
				{
					topic.Description = topic.Description.SafeHtml().ReplaceValues();
				}

				if (ModelState.IsValid)
				{
					_topicServiceClient.Create(topic, Request.UserHostAddress);
                    SubscriptionHelper.Manage(notify, topic.Id, User.Id, User.Guid, Config, _subscriptionServiceClient);
					
                    return RedirectToRoute(new { action = "Detail", controller = "Topics", id = topic.Id, name = topic.ShortName, forum = forum, page = 0 });
				}
			}
			catch (ValidationException ex)
			{
				this.AddErrors(this.ModelState, ex);
			}
            var roles = UsersServiceClient.GetRoles().Where(x => x.Key <= Role);
			ViewBag.UserRoles = new SelectList(roles, "Key", "Value");
			
            return View("Edit", topic);
		}
		#endregion

		#region Edit
		[HttpGet]
		[RequireAuthorization]
		[ValidateReadAccess]
		public ActionResult Edit(int id, string name, string forum)
		{
			Topic topic = _topicServiceClient.Get(id, name);

			if (topic == null)
			{
				return ResultHelper.NotFoundResult(this);
			}
			#region Check if user can edit
			if (this.User.Role < UserRole.Moderator && this.User.Id != topic.User.Id)
			{
				return ResultHelper.ForbiddenResult(this);
			}
			#endregion
			ViewBag.IsEdit = true;
            ViewBag.notify = SubscriptionHelper.IsUserSubscribed(id, User.Id, Config, _subscriptionServiceClient);
            var roles = UsersServiceClient.GetRoles().Where(x => x.Key <= Role);
			ViewBag.UserRoles = new SelectList(roles, "Key", "Value");

			return View(topic);
		}

		[HttpPost]
		[RequireAuthorization]
		[ValidateInput(false)]
		[ValidateAntiForgeryToken]
		public ActionResult Edit(int id, string name, string forum, [Bind(Prefix = "", Exclude = "Forum")] Topic topic, bool notify, string email)
		{
			topic.Id = id;
            topic.Forum = _forumsServiceClient.Get(forum);
			if (topic.Forum == null)
			{
				return ResultHelper.NotFoundResult(this);
			}
			if (!topic.Forum.HasPostAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}

			#region Check if user can edit
			var originalTopic = _topicServiceClient.Get(id);
			if (User.Role < UserRole.Moderator)
			{
				//If the user is not moderator or admin: Check if the user that created of the topic is the same as the logged user
				if (User.Id != originalTopic.User.Id)
				{
					return ResultHelper.ForbiddenResult(this);
				}
				//The user cannot edit the sticky property
				topic.IsSticky = originalTopic.IsSticky;
			}
			else if (!originalTopic.HasReadAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}
			#endregion

			try
			{
				SubscriptionHelper.SetNotificationEmail(notify, email, Session, Config, UsersServiceClient);

				topic.User = new User(User.Id, User.UserName);
				topic.ShortName = name;
				if (topic.Description != null)
				{
					topic.Description = topic.Description.SafeHtml().ReplaceValues();
				}
				_topicServiceClient.Edit(topic, Request.UserHostAddress);
                SubscriptionHelper.Manage(notify, topic.Id, User.Id, User.Guid, Config, _subscriptionServiceClient);
				
                return RedirectToRoute(new{action="Detail",controller="Topics",id=topic.Id,name=name,forum=forum});
			}
			catch (ValidationException ex)
			{
				this.AddErrors(this.ModelState, ex);
			}
			ViewBag.IsEdit = true;
            var roles = UsersServiceClient.GetRoles().Where(x => x.Key <= Role);
			ViewBag.UserRoles = new SelectList(roles, "Key", "Value");

			return View(topic);
		}
		#endregion

        #region Short Urls
		/// <summary>
		/// Gets the topic by id and redirects to the long relative url
		/// </summary>
		/// <param name="id"></param>
		/// <returns></returns>
		public ActionResult ShortUrl(int id)
		{
			Topic t = _topicServiceClient.Get(id);
			if (t == null)
			{
				return ResultHelper.NotFoundResult(this, true);
			}

            return RedirectToActionPermanent("Detail", "Topics", new { id = t.Id, name = t.ShortName, forum = t.Forum.ShortName });
		}
		#endregion

		#region Move / Close / Delete
		#region Delete
		[HttpPost]
		[RequireAuthorization]
		[ValidateAntiForgeryToken]
		public ActionResult Delete(int id, string name, string forum)
		{
			#region Check if user can edit
            Topic originalTopic = _topicServiceClient.Get(id);
			if (this.User.Role < UserRole.Moderator)
			{
				//Check if the user that created of the topic is the same as the logged user
				if (this.User.Id != originalTopic.User.Id)
				{
					return ResultHelper.ForbiddenResult(this);
				}
			}
			else if (!originalTopic.HasReadAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}			
			#endregion

			_topicServiceClient.Delete(id, this.User.Id, Request.UserHostAddress);

			return Json(new { nextUrl=Url.Action("Detail", "Forums", new{ forum = forum})});
		}
		#endregion

		#region Move topic
		[HttpGet]
		[RequireAuthorization(UserRole.Moderator)]
		[ValidateReadAccess]
		public ActionResult Move(int id, string name)
		{
			var topic = _topicServiceClient.Get(id, name);
			if (topic == null)
			{
				return ResultHelper.NotFoundResult(this);
			}

            ViewBag.Categories = _forumsServiceClient.GetList(User != null ? User.Id : 0);
			return View(topic);
		}

		[HttpPost]
		[RequireAuthorization(UserRole.Moderator)]
		[ValidateAntiForgeryToken]
		public ActionResult Move(int id, string name, [Bind(Prefix = "", Exclude = "Id")] Topic t)
		{
            Topic savedTopic = _topicServiceClient.Move(id, t.Forum.Id, this.User.Id, Request.UserHostAddress);
			return RedirectToAction("Detail", new
			{
				forum = savedTopic.Forum.ShortName
			});
		}
		#endregion

		#region Close
		/// <summary>
		/// Disallow replies on the topic
		/// </summary>
		[HttpPost]
		[RequireAuthorization]
		[ValidateAntiForgeryToken]
		public ActionResult CloseReplies(int id, string name)
		{
			#region Check if user can edit
            var originalTopic = _topicServiceClient.Get(id);
			if (Role < UserRole.Moderator)
			{
				//Check if the user that created of the topic is the same as the logged user
				if (this.User.Id != originalTopic.User.Id)
				{
					return ResultHelper.ForbiddenResult(this);
				}
			}
			else if (!originalTopic.HasReadAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}
			#endregion

			_topicServiceClient.Close(id, this.User.Id, Request.UserHostAddress);

			return new EmptyResult();
		}

		/// <summary>
		/// Allow replies on the topic
		/// </summary>
		[HttpPost]
		[RequireAuthorization]
		[ValidateAntiForgeryToken]
		public ActionResult OpenReplies(int id, string name)
		{
			#region Check if user can edit
			var originalTopic = _topicServiceClient.Get(id);
			if (this.User.Role < UserRole.Moderator)
			{
				//Check if the user that created of the topic is the same as the logged user
				if (this.User.Id != originalTopic.User.Id)
				{
					return ResultHelper.ForbiddenResult(this);
				}
			}
			else if (!originalTopic.HasReadAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}
			#endregion

            _topicServiceClient.Open(id, this.User.Id, Request.UserHostAddress);

			return new EmptyResult();
		}
		#endregion
		#endregion
	}
}
